/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

import java.io.IOException;

/**
 *
 * @author Ruben
 */
public class IlusParser {

    public IlusParser() {
    }

    public Token popToken(IlusLexer l) throws java.io.IOException {

        l.siguienteToken();
        Token tk = l.verToken();
        return tk;
    }

    public boolean esPuntoComa(Token t) {

        return (TipoToken.CHAR == t.getTipo() && ";".equals(t.getCadenaValor()));//";".equals(t.getCadenaValor()));
    }

    //PALABRA RESERVADA COMIENZO
    public boolean esperarProgram(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        if (TipoToken.INIPROGRAMA == t.getTipo()) {
            //Token tk = l.siguienteToken();
            //System.out.println(tk.cadenaValor);
            return esperarProcedimiento(a, popToken(l), l);
        } else {
            System.err.println("Se esperaba inicio Programa pero se encontro " + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarProcedimiento(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        if (TipoToken.PROCEDIMIENTO == t.getTipo()) {
            Token nombre = popToken(l);
            l.insertarSimbolo(nombre.getCadenaValor(), a.getProgp());
            if (esperarSentencia(a, popToken(l), l) && esperarPuntoComa1(a, popToken(l), l)) {
                a.insertar(Instruccion.REGRESAR);
                return esperarProcedimiento(a, popToken(l), l);
            } else {
                return false;
            }
        } else {
            return esperarIniEjecutar(a, t, l);
        }
    }

    public boolean esperarIniEjecutar(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        if (TipoToken.INICIOEJECUCION == t.getTipo()) {
            a.setDirInicio(a.getProgp());
            return esperarPrincipal(a, popToken(l), l);
        } else {
            System.err.println("Se esperaba inicio de ejecución, pero se encontro " + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarPrincipal(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        return (esperarListaSentencias(a, t, l)) && (esperarFinEjecutar(a, popToken(l), l));
    }

    public boolean esperarListaSentencias(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {
        if (esperarSentencia(a, t, l)) {
            //Token tk = popToken(l);
            if (esPuntoComa(popToken(l))) {
                return esperarListaSentencias(a, popToken(l), l);
            } else {
                l.sacarToken(l.verToken());
                return true;
            }
        } else {
            return false;
        }
    }

    public boolean esperarSentencia(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        if (TipoToken.INICIO == t.getTipo()) {
            return (esperarListaSentencias(a, popToken(l), l) && esperarFin(popToken(l)));
        } else if (TipoToken.REPETIR == t.getTipo()) {            
            a.insertar(Instruccion.REPETIR);
            if (esperarNumero(a, popToken(l), l)) {

                a.insertar(l.verToken().getNumeroValor());
                int saltoUbica = a.getProgp();
                a.insertar();
                if (esperarVeces(popToken(l)) && esperarSentencia(a, popToken(l), l)) {
                    a.insertar(Instruccion.REGRESAR);
                    a.insertar(saltoUbica, a.getProgp());
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } else if (TipoToken.SI == t.getTipo()) {
            if (esperarPruebaLogica(a, popToken(l), l)) {
                a.insertar(Instruccion.SUBRAMA);
                int saltoUbica = a.getProgp();
                a.insertar();
                if (esperarEntonces(popToken(l)) && esperarSentencia(a, popToken(l), l)) {
                    a.insertar(saltoUbica, a.getProgp());
                    if (esSino(popToken(l))) {
                        a.insertar(Instruccion.RAMA);
                        int saltoUbica2 = a.getProgp();
                        a.insertar();
                        a.insertar(saltoUbica, saltoUbica2 + 1);
                        if (esperarSentencia(a, popToken(l), l)) {
                            a.insertar(saltoUbica2, a.getProgp());
                            return true;
                        } else {
                            return false;
                        }
                    } else {                        
                        l.sacarToken(l.verToken());
                        return true;
                    }
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } else if (TipoToken.MIENTRAS == t.getTipo()) {
            int salto = a.getProgp();
            if (esperarPruebaLogica(a, popToken(l), l)) {
                a.insertar();
                a.insertar();                
                if (esperarHacer(popToken(l)) && esperarSentencia(a, (popToken(l)), l)) {
                    a.insertar(salto + 1, Instruccion.SUBRAMA);
                    a.insertar(salto + 2, a.getProgp() + 2);
                    a.insertar(Instruccion.RAMA);
                    a.insertar(salto);
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } else if (TipoToken.NOMBRE == t.getTipo()) {
            Simbolo sym = l.buscarSimbolo(t.getCadenaValor());
            if (sym != null) {
                a.insertar(Instruccion.LLAMADA);                
                a.insertar(sym.getUbicacion());
                return true;
            } else {
                System.err.println("Token no definido: " + t.getCadenaValor() + "\n");
                return false;
            }
        } else if (TipoToken.ACCION == t.getTipo()) {
            //String cad = t.getCadenaValor();
            //Accion accion = Accion.buscarAccion(cad);
            a.insertar(Accion.buscarAccion(t.getCadenaValor()));
            //System.out.println("Inserto accion");
            return true;
        } else {
            System.err.println("Token inesperado " + t.getCadenaValor() + " se esperaba alguna cosa");
            return false;
        }
    }

    public boolean esperarFinEjecutar(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        if (TipoToken.FINEJECUCION == t.getTipo()) {
            //Instruccion retorno = Instruccion.REGRESAR;
            a.insertar(Instruccion.REGRESAR);
            //Token tk = popToken(l);
            return esperarFinalProg(a, popToken(l), l);
        } else {
            System.err.println("Se esperaba el fin de ejecución " + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarFin(Token t) {

        TipoToken fin = TipoToken.FIN;

        return esperar(fin, t);
    }

    public boolean esperarFinalProg(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        if (TipoToken.FINALPROGRAMA == t.getTipo()) {
            Token tk = popToken(l);
            return esperarFinArchivo(a, tk, l);
        } else {
            System.err.println("Se esperafa final del programa, pero se encontro " + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarNombre(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        if (TipoToken.NOMBRE == t.getTipo()) {
            return esperarProcedimiento(a, popToken(l), l);
        } else {
            System.out.println("Se esperaba el nombre del programa, pero se encontro " + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarEntonces(Token t) {

        TipoToken entonces = TipoToken.ENTONCES;

        return esperar(entonces, t);
    }
    //PALABRA RESERVADA SI NO

    public boolean esSino(Token t) {

        return (TipoToken.SI_NO == t.getTipo());
    }

    //PALABRA RESERVADA HACER
    public boolean esperarHacer(Token t) {

        TipoToken hacer = TipoToken.HACER;

        return esperar(hacer, t);
    }

    public boolean esperarPuntoComa(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {

        if (esperarSentencia(a, t, l)) {
            if (esPuntoComa(popToken(l))) {
                return esperarListaSentencias(a, popToken(l), l);
            } else {
                l.sacarToken(l.verToken());
                return true;
            }
        } else {
            return false;
        }
    }

    public boolean esperarVeces(Token t) {

        return esperar(TipoToken.VECES, t);
    }

    public boolean esperar(TipoToken esperado, Token tk) {

        if (esperado == tk.getTipo()) {
            return true;
        } else {
            System.err.println("Se esperaba " + esperado + ", pero se encontro " + tk.getCadenaValor());
            return false;
        }
    }

    public boolean esperar(String esperado, Token tk) {


        if ((TipoToken.CHAR == tk.getTipo()) && (esperado.equals(tk.getCadenaValor()))) {
            return true;
        } else {
            System.err.println("Esperado " + esperado + ", pero se encontro " + tk.getCadenaValor());
            return false;
        }
    }

    public boolean esperarPruebaLogica(Algoritmo a, Token t, IlusLexer l) {

        if (TipoToken.DESCUBRE == t.getTipo()) {
            //Detecta accion = (Detecta) Accion.buscarAccion(t.getCadenaValor());
            a.insertar(Accion.buscarAccion(t.getCadenaValor()));
            return true;
        } else {
            System.err.println("Se esperaba prueba logica, pero se encontro " + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarNumero(Algoritmo a, Token t, IlusLexer l) {

        if (TipoToken.NUMERO == t.getTipo()) {
            return true;
        } else {
            System.err.println("Se esperaba un número, pero se encontro " + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarFinArchivo(Algoritmo a, Token t, IlusLexer l) {

        if (TipoToken.EOF == t.getTipo()) {

            return true;
        } else {
            System.err.println("Se esperaba el EOF, pero se encontro" + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarPuntoComa1(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException {
        return esperar(";", t);
    }

    public Algoritmo parser(String archivo) throws java.io.IOException {

        Algoritmo alg = new Algoritmo();
        IlusLexer IlusLexer = new IlusLexer(archivo);
        if (esperarProgram(alg, IlusLexer.verToken(), IlusLexer)) {
            return alg;
        } else {
            return null;
        }
    }
}
